package cn.changein.framework.modular.system.service;

import cn.changein.framework.base.auth.context.LoginContextHolder;
import cn.changein.framework.base.pojo.node.MenuNode;
import cn.changein.framework.base.pojo.node.ZTreeNode;
import cn.changein.framework.base.pojo.page.PageFactory;
import cn.changein.framework.core.constant.Const;
import cn.changein.framework.core.constant.factory.ConstantFactory;
import cn.changein.framework.core.constant.state.MenuStatus;
import cn.changein.framework.core.exception.enumeration.BizExceptionEnum;
import cn.changein.framework.core.listener.ConfigListener;
import cn.changein.framework.kernel.model.exception.RequestEmptyException;
import cn.changein.framework.kernel.model.exception.ServiceException;
import cn.changein.framework.modular.i18n.context.UserTranslationContext;
import cn.changein.framework.modular.i18n.model.enumeration.TranslationEnum;
import cn.changein.framework.modular.system.entity.Menu;
import cn.changein.framework.modular.system.entity.Relation;
import cn.changein.framework.modular.system.entity.Role;
import cn.changein.framework.modular.system.mapper.MenuMapper;
import cn.changein.framework.modular.system.model.MenuDTO;
import cn.changein.framework.util.ToolUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 菜单表 服务实现类
 *

 * @date: 2020-02-20
 * @copyright: 广州澄境科技有限公司
 */
@Service
public class MenuService extends ServiceImpl<MenuMapper, Menu> {

    @Resource
    private MenuMapper menuMapper;

    @Autowired
    RoleService roleService;

    @Autowired
    UserService userService;

    @Resource
    private RelationService relationService;



    /**
     * 添加菜单
     *
     * @param menuDTO
     */
    @Transactional
    public void addMenu(MenuDTO menuDTO) {
        if (menuDTO.getPid() == null || menuDTO.getPid().equals(0L)) {
            menuDTO.setAdminRoleOnly(0);
        }
        if (ToolUtil.isOneEmpty(menuDTO, menuDTO.getCode(), menuDTO.getName(), menuDTO.getPid(), menuDTO.getMenuFlag(), menuDTO.getUrl(), menuDTO.getSystemType())) {
            throw new RequestEmptyException();
        }

        //判断是否已经存在该编号
        String existedMenuName = ConstantFactory.me().getMenuNameByCode(menuDTO.getCode());
        if (ToolUtil.isNotEmpty(existedMenuName)) {
            throw new ServiceException(BizExceptionEnum.EXISTED_THE_MENU);
        }

        //组装属性，设置父级菜单编号
        Menu resultMenu = this.menuSetPcode(menuDTO);
        resultMenu.setStatus(MenuStatus.ENABLE.getCode());

        this.save(resultMenu);

        String authorityCodes = menuDTO.getAuthorityCodes();
        String authorityNames = menuDTO.getAuthorityNames();
        List<Menu> menus = new ArrayList<>();
        int menuSort = Integer.parseInt(resultMenu.getSort() + "00");
        List<Relation> relationList = new ArrayList();//给超级管理员配置新增的菜单权限
        if (StringUtils.isNotBlank(authorityCodes)) {
            String[] authorityCodeArr = authorityCodes.split(",");
            String[] authorityNamArr = authorityNames.split(",");
            for (int i = 0; i < authorityCodeArr.length; i++) {
                Menu menu = new Menu();
                menu.setCode(resultMenu.getCode() + "_" + authorityCodeArr[i]);
                menu.setPcode(resultMenu.getCode());
                menu.setPcodes(resultMenu.getPcodes() + "[" + resultMenu.getCode() + "],");
                menu.setName(resultMenu.getName() + authorityNamArr[i]);
                menu.setUrl(resultMenu.getUrl() + "/" + authorityCodeArr[i]);
                menu.setSort(menuSort);
                menu.setLevels(resultMenu.getLevels() + 1);
                menu.setMenuFlag("N");
                menu.setStatus(MenuStatus.ENABLE.getCode());
                menu.setSystemType(resultMenu.getSystemType());
                menus.add(menu);
                menuSort+= 5;
            }
            this.saveBatch(menus);
            for (Menu m : menus) {
                Relation obj = new Relation();
                obj.setMenuId(m.getMenuId());
                obj.setRoleId(Const.ADMIN_ROLE_ID);
                relationList.add(obj);
            }
        }
        Relation relation = new Relation();
        relation.setMenuId(resultMenu.getMenuId());
        relation.setRoleId(Const.ADMIN_ROLE_ID);
        relationList.add(relation);
        this.relationService.saveBatch(relationList);
    }

    /**
     * 更新菜单
     *
     * @param menuDTO
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateMenu(MenuDTO menuDTO) {

        if (menuDTO.getPid() == null || menuDTO.getPid().equals(0L)) {
            menuDTO.setAdminRoleOnly(0);
        }
        //如果菜单为空
        if (menuDTO == null || ToolUtil.isOneEmpty(menuDTO.getMenuId(), menuDTO.getCode())) {
            throw new RequestEmptyException();
        }

        //获取旧的菜单
        Long id = menuDTO.getMenuId();
        Menu menu = this.getById(id);

        if (menu == null) {
            throw new RequestEmptyException();
        }

        //设置父级菜单编号
        Menu resultMenu = this.menuSetPcode(menuDTO);

        //查找该节点的子集合,并修改相应的pcodes和level(因为修改菜单后,层级可能变化了)
        updateSubMenuLevels(menu, resultMenu);

        this.updateById(resultMenu);
    }

    /**
     * 更新所有子菜单的结构
     *
     * @param oldMenu 原来的菜单
     * @param newMenu 新菜单
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateSubMenuLevels(Menu oldMenu, Menu newMenu) {

        List<Menu> menus = menuMapper.getMenusLikePcodes(oldMenu.getCode());

        for (Menu menu : menus) {

            //更新pcode
            if (oldMenu.getCode().equals(menu.getPcode())) {
                menu.setPcode(newMenu.getCode());
            }

            //更新pcodes
            String oldPcodesPrefix = oldMenu.getPcodes() + "[" + oldMenu.getCode() + "],";
            String oldPcodesSuffix = menu.getPcodes().substring(oldPcodesPrefix.length());
            String menuPcodes = newMenu.getPcodes() + "[" + newMenu.getCode() + "]," + oldPcodesSuffix;
            menu.setPcodes(menuPcodes);

            //更新levels
            int level = StrUtil.count(menuPcodes, "[");
            menu.setLevels(level);

            //更新systemType
            menu.setSystemType(newMenu.getSystemType());

            this.updateById(menu);
        }

    }

    /**
     * 删除菜单
     *
     * @param menuId
     */
    @Transactional
    public void delMenu(Long menuId) {

        //删除菜单
        this.menuMapper.deleteById(menuId);

        //删除关联的relation
        this.menuMapper.deleteRelationByMenu(menuId);
    }

    /**
     * 删除菜单包含所有子菜单
     *
     * @param menuId
     */
    @Transactional
    public void delMenuContainSubMenus(Long menuId) {

        Menu menu = menuMapper.selectById(menuId);

        //删除当前菜单
        delMenu(menuId);

        //删除所有子菜单
        List<Menu> menus = menuMapper.getMenusLikePcodes(menu.getCode());

        for (Menu temp : menus) {
            delMenu(temp.getMenuId());
        }
    }

    /**
     * 根据条件查询菜单
     *
     * @param condition
     * @param level
     * @param menuId
     * @return
     */
    public Page<Map<String, Object>> selectMenus(String condition, String level, Long menuId) {

        //获取menuId的code
        String code = "";
        if (menuId != null && menuId != 0L) {
            Menu menu = this.getById(menuId);
            code = menu.getCode();
        }

        Page page = PageFactory.defaultPage();

        return this.baseMapper.selectMenus(page, condition, level, menuId, code);
    }

    /**
     * 根据条件查询菜单
     *
     * @param roleId
     * @return
     */
    public List<Long> getMenuIdsByRoleId(Long roleId) {
        return this.baseMapper.getMenuIdsByRoleId(roleId);
    }

    /**
     * 获取菜单列表树
     */
    public List<ZTreeNode> menuTreeList() {
        return this.baseMapper.menuTreeList();
    }

    /**
     * 获取菜单列表树
     *
     * @param menuIds
     * @return
     */
    public List<ZTreeNode> menuTreeListByMenuIds(List<Long> menuIds) {
        return this.baseMapper.menuTreeListByMenuIds(menuIds);
    }

    /**
     * 删除menu关联的relation
     *
     * @param menuId
     * @return
     */
    public int deleteRelationByMenu(Long menuId) {
        return this.baseMapper.deleteRelationByMenu(menuId);
    }

    /**
     * 获取资源url通过角色id
     *
     * @param roleId
     * @return
     */
    public List<String> getResUrlsByRoleId(Long roleId) {
        return this.baseMapper.getResUrlsByRoleId(roleId);
    }

    /**
     * 根据角色获取菜单
     *
     * @param roleIds
     * @return
     */
    public List<MenuNode> getMenusByRoleIds(List<Long> roleIds, String menuFlag) {
        //当父级角色的权限被移除时，子角色的权限要移除
        List<Role> roleList = roleService.listByIds(roleIds);
        if (CollectionUtil.isEmpty(roleList)) {
            return Lists.newArrayList();
        }
        roleList = roleList.stream().sorted(Comparator.comparingInt(Role::getRoleLevel)).collect(Collectors.toList());
        Map<Integer, List<Role>> roleLevelMap = roleList.stream().collect(Collectors.groupingBy(Role::getRoleLevel));
        //父类的角色集合
        List<Role> parentRoleList = roleLevelMap.get(roleList.get(0).getRoleLevel());
        roleIds = parentRoleList.stream().map(Role::getRoleId).collect(Collectors.toList());
        List<MenuNode> menus = this.baseMapper.getMenusByRoleIds(roleIds, menuFlag);

        //给所有的菜单url加上ctxPath
        for (MenuNode menuItem : menus) {
            menuItem.setUrl(ConfigListener.getConf().get("contextPath") + menuItem.getUrl());

            //如果是当前用户选择语言是中文，则返回菜单名字，而不是字典名字
            if (UserTranslationContext.getUserCurrentTrans().equals(TranslationEnum.CHINESE)) {
                continue;
            } else {
                //将菜单进行多语言翻译
                String transName = UserTranslationContext.get("MENU_" + menuItem.getCode().toUpperCase());
                menuItem.setName(transName);
            }
        }


        return menus;
    }

    /**
     * 根据code查询菜单
     *
     * @param code
     * @return
     */
    public Menu selectByCode(String code) {
        Menu menu = new Menu();
        menu.setCode(code);
        QueryWrapper<Menu> queryWrapper = new QueryWrapper<>(menu);
        return this.baseMapper.selectOne(queryWrapper);
    }

    /**
     * 根据请求的父级菜单编号设置pcode和层级
     *
     * @param menuParam
     * @return
     */
    public Menu menuSetPcode(MenuDTO menuParam) {

        Menu resultMenu = new Menu();
        BeanUtil.copyProperties(menuParam, resultMenu);

        if (ToolUtil.isEmpty(menuParam.getPid()) || menuParam.getPid().equals(0L)) {
            resultMenu.setPcode("0");
            resultMenu.setPcodes("[0],");
            resultMenu.setLevels(1);
        } else {
            Long pid = menuParam.getPid();
            Menu pMenu = this.getById(pid);
            Integer pLevels = pMenu.getLevels();
            resultMenu.setPcode(pMenu.getCode());

            //如果编号和父编号一致会导致无限递归
            if (menuParam.getCode().equals(menuParam.getPcode())) {
                throw new ServiceException(BizExceptionEnum.MENU_PCODE_COINCIDENCE);
            }

            resultMenu.setLevels(pLevels + 1);
            resultMenu.setPcodes(pMenu.getPcodes() + "[" + pMenu.getCode() + "],");
        }

        return resultMenu;
    }

    /**
     * 获取菜单树形列表
     *
     * @param condition
     * @param level
     * @return
     */
    public List<Map<String, Object>> selectMenuTree(String condition, String level) {
        List<Map<String, Object>> maps = this.baseMapper.selectMenuTree(condition, level);

        if (maps == null) {
            maps = new ArrayList<>();
        }

        //修复菜单查询bug，带条件的暂时先父级置为0
        if (ToolUtil.isNotEmpty(condition) || ToolUtil.isNotEmpty(level)) {
            if (maps.size() > 0) {

                //将pcode置为root
                for (Map<String, Object> menu : maps) {
                    menu.put("pcode", "0");
                }
            }
        }

        //创建根节点
        /*Menu menu = new Menu();
        menu.setMenuId(-1L);
        menu.setName("根节点");
        menu.setCode("0");
        menu.setPcode("-2");
        maps.add(BeanUtil.beanToMap(menu));*/

        return maps;
    }

    /**
     * 角色权限过滤:权限继承，管理员专属权限
     */
    public void filterMenus(List<ZTreeNode> zTreeNodes, Role role) {
        if (!Objects.equals(role.getAdminFlag(), 1)) {
            //没有管理员角色
            List<Long> ids = zTreeNodes.stream().map(ZTreeNode::getId).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(ids)) {
                List<Menu> menus = super.listByIds(ids);
                List<Long> adminRoleOnlyMenuIds = menus.stream().filter(menu -> {
                    return menu.getAdminRoleOnly() == 1;
                }).map(Menu::getMenuId).collect(Collectors.toList());
                zTreeNodes.removeIf(next -> adminRoleOnlyMenuIds.contains(next.getId()));
            }
        }

        //当前用户的所有可以支配的角色
        List<Long> roleIdList = LoginContextHolder.getContext().getUser().getRoleList();
        List<Role> roleList = roleService.listByIds(roleIdList);
        boolean adminFlag = roleList.stream().anyMatch(temp -> temp.getAdminFlag() == 1);
        if (!adminFlag) {
            List<MenuNode> menusByRoles = this.getMenusByRoleIds(roleList.stream().map(Role::getRoleId).collect(Collectors.toList()), null);
            List<Long> menuIds = menusByRoles.stream().map(MenuNode::getId).collect(Collectors.toList());
            zTreeNodes.removeIf(zTreeNode -> !menuIds.contains(zTreeNode.getId()));
        }
    }
}

